/* class */
import ValidateResult from '../BaseCalculationFormula/ValidateResult';
import { commonValidate } from '../BaseCalculationFormula/validate';
/* enum */
import { SignOperatorEnum, CalculationFormulaItem, FormulaFuncTipEum, SignOperators } from './formulaEnum';
import i18n from '@src/locales';

/**
 * @description 验证表达式的正确性
 * 基本用正则，先 test 验证时因为速度较快，后面用 exec 速度稍慢，但是可以获得更多信息
 * FIXME: /g 匹配有问题，没深究，应该可临时解决(暂未解决)
 * @param {String} str 验证的字符串
 * @param {String} defaultNumber 默认数字，字段名字替代的
 */
export function validate(str: string, defaultNumber?: number | string) {
  // 连续运算符
  const ContinuousReg = /[+\-*/]{2,}/;
  if (ContinuousReg.test(str)) {
    return commonValidate(
      ContinuousReg,
      str,
      i18n.t('common.form.tip.forMula.tips7') as string
    );
  }


  const ContinuousFieldsReg = /(field){2,}/;
  if (ContinuousFieldsReg.test(str)) {
    return commonValidate(
      ContinuousFieldsReg,
      str,
      // 运算对象不能连续出现, 请在中间插入运算符号
      i18n.t('common.form.tip.forMula.tips8') as string
    );
  }

  const NumContactFieldsReg = /\d((field)|(fun))|((field)(fun)){1,}/;
  if (NumContactFieldsReg.test(str)) {
    return commonValidate(
      NumContactFieldsReg,
      str,
      // 运算对象或者函数前不能直接跟数字或者对象, 请在中间插入运算符号
      i18n.t('common.form.preview.forMula.tip4') as string
    );
  }

  // 函数是否有括号
  const funWithBracketReg = /fun(?!\(\))$/;
  if (funWithBracketReg.test(str)) {
    return commonValidate(
      funWithBracketReg,
      str,
      i18n.t('common.form.preview.forMula.tip5') as string
    );
  }
  // 检测函数中表达式是否存在括号（基于有后面的括号检测保底的情况下~）
  if(Array.from(str.matchAll(/fun\(([^)]*?)\)/g)).find(v => v[1]?.includes('('))) {
    return ValidateResult.fail(i18n.t('common.form.preview.forMula.tip11') as string, 0, str);
  }

  const regex = /fun\(([^)]+)/g;
  const matchFunStrArr = Array.from(str.matchAll(regex));
  const errorArr: any = [];

  matchFunStrArr.forEach(item => {
    const funStr = item[1];
    // if (/[+\-*/]/g.test(funStr)) {
    //   return errorArr.push(ValidateResult.fail(i18n.t('common.form.preview.forMula.tip6') as string, 0, funStr));
    // }
    if (/^,/g.test(funStr)) {
      return errorArr.push(ValidateResult.fail(i18n.t('common.form.preview.forMula.tip7') as string, 0, funStr));
    }
    if (/,$/g.test(funStr)) {
      return errorArr.push(ValidateResult.fail(i18n.t('common.form.preview.forMula.tip8') as string, 0, funStr));
    }
    if (funStr.includes('fun')) {
      return errorArr.push(ValidateResult.fail(i18n.t('common.form.preview.forMula.tip9') as string, 0, funStr));
    }
  });
  if(errorArr.length > 0) return errorArr[0];

  // 空括号
  const EmptyBracketReg = /\(\)/;
  if (EmptyBracketReg.test(str)) {
    return commonValidate(
      EmptyBracketReg,
      str,
      i18n.t('common.form.tip.forMula.tips9') as string
    );
  }

  // 左括号( 后面是运算符
  const LeftBracketAfterWithFormulaReg = /\([+\-*/]/;
  if (LeftBracketAfterWithFormulaReg.test(str)) {
    return commonValidate(
      LeftBracketAfterWithFormulaReg,
      str,
      i18n.t('common.form.tip.forMula.tips10') as string
    );
  }

  // 右括号 ) 前面是运算符
  // /\bSUM\b//\bAVERAGE\b//\bADD\b//\bPRODUCT\b//\bMAX\b//\bMIN\b/
  const RightBracketBeforeWithFormulaReg = /[+\-*/]\)/;
  if (RightBracketBeforeWithFormulaReg.test(str)) {
    return commonValidate(
      RightBracketBeforeWithFormulaReg,
      str,
      i18n.t('common.form.tip.forMula.tips11') as string
    );
  }


  // 左括号 ( 前面不是运算符
  const LeftBracketBeforeReg = /[^+\-*/()fun]\(/;
  // const leftBracketIndex = str.indexOf('(');
  if (LeftBracketBeforeReg.test(str) && str[0] != '(') {
    return commonValidate(
      LeftBracketBeforeReg,
      str,
      i18n.t('common.form.preview.forMula.tip10') as string
    );
  }

  // 右括号 ) 后面不是运算符
  const RightBracketAfterReg = /\)[^+\-*/)]/;
  if (RightBracketAfterReg.test(str)) {
    return commonValidate(
      RightBracketAfterReg,
      str,
      i18n.t('common.form.tip.forMula.tips13') as string
    );
  }

  // 长度为1 且 是符号
  if (str.length === 1 && ['+', '-', '*', '/', '(', ')'].includes(str)) {
    return ValidateResult.fail(i18n.t('common.form.tip.forMula.tips14') as string, 0, str);
  }

  // 运算符开头
  const startWithReg = /^[*/+-]/;
  if (startWithReg.test(str)) {
    return commonValidate(startWithReg, str, i18n.t('common.form.tip.forMula.tips15') as string);
  }

  // 运算符结尾
  const endWithReg = /[*/+-]$/;
  if (endWithReg.test(str)) {
    return commonValidate(endWithReg, str, i18n.t('common.form.tip.forMula.tips16') as string);
  }

  /** 错误情况，括号不配对 */
  // 括号堆栈信息
  const leftBracketStack = [];
  // 左括号索引
  let leftBracketIndex = 0;

  for (let i = 0; i < str.length; i++) {
    const item = str.charAt(i);
    // 左括号
    if (SignOperatorEnum.LeftBracket === item) {
      leftBracketStack.push(SignOperatorEnum.LeftBracket);
      leftBracketIndex = i;
    }
    // 右括号
    else if (SignOperatorEnum.RightBracket === item) {
      // 如果堆栈中存在左括号 则删除
      if (leftBracketStack.length > 0) {
        leftBracketStack.pop();
      } else {
        return ValidateResult.fail(
          i18n.t('common.form.tip.forMula.tips17') as string,
          i,
          SignOperatorEnum.RightBracket
        );
      }
    }
  }


  // 如果堆栈中存在左括号，则说明没有匹配的右括号
  if (leftBracketStack.length > 0) {
    return ValidateResult.fail(
      i18n.t('common.form.tip.forMula.tips17') as string,
      leftBracketIndex,
      SignOperatorEnum.LeftBracket
    );
  }

  return ValidateResult.succ();
}

/**
 * @description 主要验证表达式类型的正确性，类型检查需在常规的validate之后，确定基础的格式正常的情况下调用的~(目前主要是质保规则需要，其他有需要的自行看情况)
 * @param {String} str 验证的字符串
 * @param {String} defaultNumber 默认数字，字段名字替代的
 */
export function validateType(formulaList: CalculationFormulaItem[], spectialArr:string[]) {
  // 去掉空格，再做个转化
  let formulaText = formulaList.filter(v => v.value).map(v => {
    const typeObj:Record<string, string> = {
      'date': 'timeField',
      'datetime': 'timeField',
    }
    // 此处暂不考虑该项为计算公式类型的情况
    const type = v.formType ? (typeObj[v.formType] || 'numField') : ''
    let value = /\d+/.test(v.value) ? 'numField' : v.value
    // 如果不是操作符和整数，按照非法字符处理
    const operations = Object.keys(FormulaFuncTipEum).concat(SignOperators).concat([',']);
    if(!operations.includes(v.value) && !/^-?\d+$/.test(v.value) ) value = 'chartField'
    return type || value
  }).join('')
  // 判断是否有非法字符输入
  const chartReg = /chartField/
  if(chartReg.test(formulaText)) {
    return ValidateResult.fail(i18n.t('common.form.preview.materialWarehouse.tip1') as string);
  }
  // 下面的判断大多都是在前面判断的基础上去判断的，所以有改动需要注意下有没有影响~
  const timeReg = /timeField/
  if (spectialArr.includes('mustOneTime') && !timeReg.test(formulaText)) {
    return ValidateResult.fail(i18n.t('component.baseCalculationFormulaSpectial.validTips2') as string);
  }

  // 函数检查
  const regex = /(MAX|MIN|ADD)\(([^)]+)\)/g;
  const matchFunStrArr = Array.from(formulaText.matchAll(regex));
  // 对于匹配到的函数做个循环
  for (const matchFunStrItem of matchFunStrArr) {
    const paramsStr = matchFunStrItem[2]; // 函数内容
    const params = paramsStr.split(',').map(v => v.trim()); // 具体参数的数组
    const funName = matchFunStrItem[1]; // 函数名
    for (const i in params) {
      const param = params[i]
      // 运算符开头
      const startWithReg = /^[*/+-]/;
      if (startWithReg.test(param)) {
        return ValidateResult.fail(i18n.t('common.form.preview.forMula.tip12') as string);
      }
      
      // 运算符结尾
      const endWithReg = /[*/+-]$/;
      if (endWithReg.test(param)) {
        return ValidateResult.fail(i18n.t('common.form.preview.forMula.tip13') as string);
      }
      // 对于参数的计算做个校验
      const err = validCount(param)
      if(err) return err
      params[i] = param.includes('timeField') ? 'timeField' : 'numField'
    }
    // 检测ADD函数中是否存在时间相加的情况
    // const notOneTimeReg = /.*date.*date.*/;
    if (funName === 'ADD' && params.filter(v => v === 'timeField').length > 1) {
      // return commonValidate(notOneTimeReg, paramsStr, '时间不能相加' as string);
      return ValidateResult.fail(i18n.t('component.baseCalculationFormulaSpectial.validTips3') as string);
    }
    // 检测MAX及MIN函数中是否存在格式不相同的情况
    // const sameTypeReg = /^(\w+)\s*,\s*(\1,?)+$/;
    if (/(MAX|MIN)/.test(funName) && spectialArr.includes('minMaxSameType') && params.find(v => v !== params[0])) {
      return ValidateResult.fail(i18n.t('component.baseCalculationFormulaSpectial.validTips4') as string);
    }
  };

  // 把函数做层转化，如果存在时间字段则视为时间字段，不存在就视为numField字段
  formulaText = formulaText.replaceAll(/(MAX|MIN|ADD)\(([^)]+)\)/g, (str, ...rest) => {
    return str.includes('timeField') ? 'timeField' : 'numField'
  })

  // 对于计算方面做个校验
  const err = validCount(formulaText)
  if(err) return err

  return ValidateResult.succ();
}

/** 计算校验 */
function validCount(formulaText: string) {
  // 存在多个timeField说明时间之间存在计算
  const timeCountReg = /timeField.*timeField/;
  if (timeCountReg.test(formulaText)) {
    return commonValidate(timeCountReg, formulaText, i18n.t('component.baseCalculationFormulaSpectial.validTips5') as string);
  }
  const numAndTimeCountCheck = (str:string) => {
    // 在不包含括号的前提下
    const regArr = [{
      // 包含时间字段时，如果数字字段在前，并且存在减号，就说明存在数字字段-时间字段的情况
      reg: /numField.*-.*timeField/,
      tip: i18n.t('component.baseCalculationFormulaSpectial.validTips6') as string,
    }, {
      // 检查数字和时间之间乘除的情况
      reg: /numField\s*[*/]\s*timeField|timeField\s*[*/]\s*numField/,
      tip: i18n.t('component.baseCalculationFormulaSpectial.validTips1') as string,
    }]
    const res = regArr.find(v => v.reg.test(str))
    if (res) return res.tip
  }

  try {
    let text = formulaText
    while(text.includes('(')) {
      text = text.replaceAll(/\([^()]*\)/g, (str, ...rest) => {
        if(str.includes('timeField')) {
          const tip = numAndTimeCountCheck(str)
          if(tip) throw new Error(tip)
          // 没问题就视为时间字段
          return 'timeField'
        }
        return 'numField'
      })
    }
    // 最外层的检查
    const tip = numAndTimeCountCheck(text)
    if(tip) throw new Error(tip)
  } catch (error:any) {
    return ValidateResult.fail(error?.message || '');
  }
}